/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Collections.ObjectModel;
using System.Xml.XPath;
using DaveSexton.DocProject.Sandcastle.Resources;

namespace DaveSexton.DocProject.Sandcastle.TopicManagement
{
	public abstract class ApiFilterConfigNode
	{
		#region Public Properties
		public XPathNavigator Node
		{
			get
			{
				return node.Clone();
			}
		}

		public XPathNavigator Parent
		{
			get
			{
				return parent;
			}
		}

		public TopicType TopicType
		{
			get
			{
				return topicType;
			}
		}

		public string Name
		{
			get
			{
				return GetName(this.node);
			}
			private set
			{
				SetName(node, value);
			}
		}

		public bool IsExposed
		{
			get
			{
				return IsNodeExposed(this.node);
			}
			set
			{
				Expose(node, value);
			}
		}
		#endregion

		#region Private / Protected
		private XPathNavigator node;
		private readonly XPathNavigator parent;
		private readonly TopicType topicType;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="ApiFilterConfigNode" /> class for derived classes and appends the xml node to the specified <paramref name="parent" />.
		/// </summary>
		protected ApiFilterConfigNode(string elementName, ApiFilterConfigNode parent, TopicType topicType, string name, bool expose)
		{
			if (parent == null)
				// derived class throws exception
				return;

			if (elementName == null)
				throw new ArgumentNullException("elementName");

			if (!Enum.IsDefined(typeof(TopicType), topicType))
				throw new System.ComponentModel.InvalidEnumArgumentException("topicType", (int) topicType, typeof(TopicType));

			if (name == null)
				throw new ArgumentNullException("name");

			this.topicType = topicType;
			this.parent = parent.node.Clone();

			node = parent.node.Clone();
			node.AppendChildElement(null, elementName, null, null);
			node = node.SelectSingleNode(elementName + "[last()]");

			// set properties, not fields
			Name = name;
			IsExposed = expose;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="ApiFilterConfigNode" /> class for derived classes and appends the xml node to the specified <paramref name="parent" />.
		/// </summary>
		protected ApiFilterConfigNode(string elementName, XPathNavigator parent, TopicType topicType, string name, bool expose)
		{
			if (parent == null)
				// derived class throws exception
				return;

			if (elementName == null)
				throw new ArgumentNullException("elementName");

			if (!Enum.IsDefined(typeof(TopicType), topicType))
				throw new System.ComponentModel.InvalidEnumArgumentException("topicType", (int) topicType, typeof(TopicType));

			if (name == null)
				throw new ArgumentNullException("name");

			this.topicType = topicType;
			this.parent = parent;

			node = parent.Clone();
			node.AppendChildElement(null, elementName, null, null);
			node = node.SelectSingleNode(elementName + "[last()]");
			
			// set properties, not fieldsnode
			Name = name;
			IsExposed = expose;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="ApiFilterConfigNode" /> class for derived classes amd associates it with the specified xml <paramref name="node" />.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1805:DoNotInitializeUnnecessarily")]
		protected ApiFilterConfigNode(IXPathNavigable node, TopicType topicType)
		{
			if (node == null)
				throw new ArgumentNullException("node");

			this.node = node.CreateNavigator();
			this.topicType = topicType;

			parent = this.node.Clone();

			if (!parent.MoveToParent())
				parent = null;
		}
		#endregion

		#region Methods
		private static bool IsNodeExposed(XPathNavigator node)
		{
			node = node.Clone();

			if (node.MoveToAttribute("expose", "") && !string.IsNullOrEmpty(node.Value))
				return node.Value.Equals("true", StringComparison.OrdinalIgnoreCase);
			else
				throw new InvalidOperationException(string.Format(System.Globalization.CultureInfo.CurrentCulture,
					Errors.MRefBuilderConfigMissingExposeAttribute, Environment.NewLine, node.Name, GetName(node)));
		}

		internal static void Expose(XPathNavigator node, bool expose)
		{
			string value = (expose) ? "true" : "false";

			node = node.Clone();

			if (!node.MoveToAttribute("expose", ""))
				node.CreateAttribute(null, "expose", null, value);
			else
				node.SetValue(value);
		}

		private static string GetName(XPathNavigator node)
		{
			node = node.Clone();

			if (node.MoveToAttribute("name", ""))
				return node.Value;
			else
				throw new InvalidOperationException(string.Format(System.Globalization.CultureInfo.CurrentCulture,
					Errors.MRefBuilderConfigMissingNameAttribute, Environment.NewLine, node.Name));
		}

		private static void SetName(XPathNavigator node, string name)
		{
			node = node.Clone();

			if (!node.MoveToAttribute("name", ""))
				node.CreateAttribute(null, "name", null, name);
			else
				node.SetValue(name);
		}

		public override string ToString()
		{
			return string.Format(System.Globalization.CultureInfo.CurrentCulture,
				"<{0} name=\"{1}\" expose=\"{2}\" />", node.Name, Name, IsExposed);
		}
		#endregion
	}

	public sealed class ApiFilterConfigNodeCollection<T> : Collection<T>
		where T : ApiFilterConfigNode
	{
		#region Public Properties
		public T this[string name]
		{
			get
			{
				foreach (T node in this)
					if (node.Name.Equals(name, StringComparison.Ordinal))
						return node;

				return null;
			}
		}
		#endregion

		#region Private / Protected
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="ApiFilterConfigNodeCollection" /> class.
		/// </summary>
		public ApiFilterConfigNodeCollection()
		{
		}
		#endregion

		#region Methods
		protected override void InsertItem(int index, T item)
		{
			if (item == null)
				throw new ArgumentNullException("item");

			if (Contains(item))
				throw new InvalidOperationException(Errors.ApiFilterConfigNodeAlreadyContained);

			base.InsertItem(index, item);
		}

		protected override void SetItem(int index, T item)
		{
			if (item == null)
				throw new ArgumentNullException("item");

			if (IndexOf(item) != index && Contains(item))
				throw new InvalidOperationException(Errors.ApiFilterConfigNodeAlreadyContained);

			base.SetItem(index, item);
		}
		#endregion
	}
}
